package org.destinyshine.cricket.connector;

import org.destinyshine.cricket.utils.ChannelUtils;
import org.destinyshine.cricket.utils.CharUtils;
import org.destinyshine.cricket.utils.MultiMap;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.util.*;

/**
 * Created by destinyliu on 2016/2/20.
 */
public class HttpServletResponseImpl implements HttpServletResponse {

    private SocketChannel socketChannel;

    private final WritableByteChannel nativeChannel;
    private final ByteBuffer contentBuffer;
    private ServletOutputStream servletOutputStream;
    private PrintWriter servletWriter;

    private int status;

    private MultiMap<String, String> headers = new MultiMap<String, String>() {};

    public HttpServletResponseImpl(WritableByteChannel channel) {
        this.nativeChannel = channel;
        this.contentBuffer = ByteBuffer.allocate(1024 * 16);
    }

    @Override
    public void addCookie(Cookie cookie) {

    }

    @Override
    public boolean containsHeader(String s) {
        return false;
    }

    @Override
    public String encodeURL(String s) {
        return null;
    }

    @Override
    public String encodeRedirectURL(String s) {
        return null;
    }

    @Override
    public String encodeUrl(String s) {
        return null;
    }

    @Override
    public String encodeRedirectUrl(String s) {
        return null;
    }

    @Override
    public void sendError(int i, String s) throws IOException {

    }

    @Override
    public void sendError(int i) throws IOException {

    }

    @Override
    public void sendRedirect(String s) throws IOException {

    }

    @Override
    public void setDateHeader(String s, long l) {

    }

    @Override
    public void addDateHeader(String name, long date) {

    }

    @Override
    public void setIntHeader(String name, int value) {
        setHeader(name, String.valueOf(value));
    }

    @Override
    public void addIntHeader(String name, int value) {
        addHeader(name, String.valueOf(value));
    }

    public String getHeader(String name) {
        return headers.get(name);
    }

    @Override
    public void setHeader(String name, String value) {
        headers.set(name, value);
    }

    @Override
    public void addHeader(String name, String value) {
        headers.add(name, value);
    }

    @Override
    public void setStatus(int status) {
        this.status = status;
    }

    @Override
    public void setStatus(int status, String message) {
        this.status = status;
    }

    @Override
    public String getCharacterEncoding() {
        return null;
    }

    @Override
    public String getContentType() {
        return getHeader("Content-Type");
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (this.servletOutputStream == null) {

            this.servletOutputStream = new ServletOutputStream() {
                @Override
                public void write(int b) throws IOException {
                    contentBuffer.put((byte) b);
                }

                @Override
                public void flush() throws IOException {
                    //contentBuffer.flip();
                    //nativeChannel.write(Arrays.copyOf(buffer.array(), buffer.limit()));
                    //nativeChannel.flush();
                    //contentBuffer.clear();
                }
            };

        }
        return servletOutputStream;
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        getOutputStream();
        if (this.servletWriter == null) {
            this.servletWriter = new PrintWriter(this.servletOutputStream, true) {
                @Override
                public void flush() {
                    super.flush();
                }
            };
        }
        return this.servletWriter;
    }

    @Override
    public void setCharacterEncoding(String encoding) {

    }

    @Override
    public void setContentLength(int contentLength) {
        this.setIntHeader("Content-Length", contentLength);
    }

    @Override
    public void setContentType(String contentType) {

    }

    @Override
    public void setBufferSize(int bufferSize) {
        this.contentBuffer.limit(bufferSize);
    }

    @Override
    public int getBufferSize() {
        return this.contentBuffer.limit();
    }

    @Override
    public void flushBuffer() throws IOException {
        //this.getOutputStream().flush();
    }

    @Override
    public void resetBuffer() {
        this.contentBuffer.reset();
    }

    @Override
    public boolean isCommitted() {
        return false;
    }

    @Override
    public void reset() {
        resetBuffer();
        this.headers.clear();
        this.status = SC_OK;
    }

    @Override
    public void setLocale(Locale locale) {

    }

    @Override
    public Locale getLocale() {
        return null;
    }

    public void completeResponse() {
        StringBuilder headerBuilder = new StringBuilder();
        this.contentBuffer.flip();
        this.setContentLength(this.contentBuffer.limit());
        headerBuilder.append("HTTP/1.1 200 OK \r\n");

        for (Map.Entry<String, List<String>> headerEntry : headers.unmodifiableInternalMap().entrySet()) {
            headerBuilder.append(headerEntry.getKey());
            headerBuilder.append(':');
            for (String headerValue : headerEntry.getValue()) {
                headerBuilder.append(' ').append(headerValue).append(',');
            }
            headerBuilder.setCharAt(headerBuilder.length() - 1, '\r');
            headerBuilder.append('\n');
        }
        headerBuilder.append('\r').append('\n');
        ByteBuffer headerBuffer = ByteBuffer.wrap(CharUtils.toASCIIByteArray(headerBuilder));

        try {
            ChannelUtils.write(nativeChannel, headerBuffer, contentBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
